﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using QuickTest.Core.Events;
using QuickTest.Core.TestRunner;

namespace QuickTest.UI.Controls
{
    /// <summary>
    /// ColorProgressBar provides a custom progress bar with the
    /// ability to control the color of the bar and to render itself
    /// in either solid or segmented style. The bar can be updated
    /// on the fly and has code to avoid repainting the entire bar
    /// when that occurs.
    /// </summary>
    public partial class ColorProgressBar : System.Windows.Forms.Control
    {
        #region Instance Variables
        /// <summary>
        /// The current progress value
        /// </summary>
        private int val = 0;

        /// <summary>
        /// The minimum value allowed
        /// </summary>
        private int min = 0;

        /// <summary>
        /// The maximum value allowed
        /// </summary>
        private int max = 100;

        /// <summary>
        /// Amount to advance for each step
        /// </summary>
        private int step = 1;

        /// <summary>
        /// Last segment displayed when displaying asynchronously rather 
        /// than through OnPaint calls.
        /// </summary>
        private int lastSegmentCount = 0;

        /// <summary>
        /// The brush to use in painting the progress bar
        /// </summary>
        private Brush foreBrush = null;

        /// <summary>
        /// The brush to use in painting the background of the bar
        /// </summary>
        private Brush backBrush = null;

        /// <summary>
        /// Indicates whether to draw the bar in segments or not
        /// </summary>
        private bool segmented = false;

        #endregion

        #region Constructors & Disposer
        public ColorProgressBar()
        {
            // This call is required by the Windows.Forms Form Designer.
            InitializeComponent();

            SetStyle(ControlStyles.ResizeRedraw, true);
        }

        /// <summary> 
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (components != null)
                {
                    components.Dispose();
                }
                this.ReleaseBrushes();
            }
            base.Dispose(disposing);
        }
        #endregion

        #region Properties

        [Category("Behavior")]
        public int Minimum
        {
            get { return this.min; }
            set
            {
                if (value <= Maximum)
                {
                    if (this.min != value)
                    {
                        this.min = value;
                        this.PaintBar();
                    }
                }
                else
                {
                    throw new ArgumentOutOfRangeException("Minimum", value
                        , "Minimum must be <= Maximum.");
                }
            }
        }

        [Category("Behavior")]
        public int Maximum
        {
            get { return this.max; }
            set
            {
                if (value >= Minimum)
                {
                    if (this.max != value)
                    {
                        this.max = value;
                        this.PaintBar();
                    }
                }
                else
                {
                    throw new ArgumentOutOfRangeException("Maximum", value
                        , "Maximum must be >= Minimum.");
                }
            }
        }

        [Category("Behavior")]
        public int Step
        {
            get { return this.step; }
            set
            {
                if (value <= Maximum && value >= Minimum)
                {
                    this.step = value;
                }
                else
                {
                    throw new ArgumentOutOfRangeException("Step", value
                        , "Must fall between Minimum and Maximum inclusive.");
                }
            }
        }

        [Browsable(false)]
        private float PercentValue
        {
            get
            {
                if (0 != Maximum - Minimum) // NRG 05/28/03: Prevent divide by zero
                    return ((float)this.val / ((float)Maximum - (float)Minimum));
                else
                    return (0);
            }
        }

        [Category("Behavior")]
        public int Value
        {
            get { return this.val; }
            set
            {
                if (value == this.val)
                    return;
                else if (value <= Maximum && value >= Minimum)
                {
                    this.val = value;
                    this.PaintBar();
                }
                else
                {
                    throw new ArgumentOutOfRangeException("Value", value
                        , "Must fall between Minimum and Maximum inclusive.");
                }
            }
        }

        [Category("Appearance")]
        public bool Segmented
        {
            get { return segmented; }
            set { segmented = value; }
        }

        #endregion

        #region Methods

        protected override void OnCreateControl()
        {
        }

        public void PerformStep()
        {
            int newValue = Value + Step;

            if (newValue > Maximum)
                newValue = Maximum;

            Value = newValue;
        }

        protected override void OnBackColorChanged(System.EventArgs e)
        {
            base.OnBackColorChanged(e);
            this.Refresh();
        }
        protected override void OnForeColorChanged(System.EventArgs e)
        {
            base.OnForeColorChanged(e);
            this.InvokeIfNeeded(() => this.Refresh());
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            this.lastSegmentCount = 0;
            this.ReleaseBrushes();
            PaintBar(e.Graphics);
            ControlPaint.DrawBorder3D(
                e.Graphics
                , this.ClientRectangle
                , Border3DStyle.SunkenOuter);
            //e.Graphics.Flush();
        }

        private void ReleaseBrushes()
        {
            if (foreBrush != null)
            {
                foreBrush.Dispose();
                backBrush.Dispose();
                foreBrush = null;
                backBrush = null;
            }
        }

        private void AcquireBrushes()
        {
            if (foreBrush == null)
            {
                foreBrush = new SolidBrush(this.ForeColor);
                backBrush = new SolidBrush(this.BackColor);
            }
        }

        private void PaintBar()
        {
            using (Graphics g = this.CreateGraphics())
            {
                this.PaintBar(g);
            }
        }

        private void PaintBar(Graphics g)
        {
            Rectangle theBar = Rectangle.Inflate(ClientRectangle, -2, -2);
            int maxRight = theBar.Right - 1;
            this.AcquireBrushes();

            if (segmented)
            {
                int segmentWidth = (int)((float)ClientRectangle.Height * 0.66f);
                int maxSegmentCount = (theBar.Width + segmentWidth) / segmentWidth;

                //int maxRight = Bar.Right;
                int newSegmentCount = (int)System.Math.Ceiling(PercentValue * maxSegmentCount);
                if (newSegmentCount > lastSegmentCount)
                {
                    theBar.X += lastSegmentCount * segmentWidth;
                    while (lastSegmentCount < newSegmentCount)
                    {
                        theBar.Width = System.Math.Min(maxRight - theBar.X, segmentWidth - 2);
                        g.FillRectangle(foreBrush, theBar);
                        theBar.X += segmentWidth;
                        lastSegmentCount++;
                    }
                }
                else if (newSegmentCount < lastSegmentCount)
                {
                    theBar.X += newSegmentCount * segmentWidth;
                    theBar.Width = maxRight - theBar.X;
                    g.FillRectangle(backBrush, theBar);
                    lastSegmentCount = newSegmentCount;
                }
            }
            else
            {
                g.FillRectangle( backBrush, theBar );
                theBar.Width = theBar.Width * val / max;
                g.FillRectangle(foreBrush, theBar);
            }

            if (Value == Minimum || Value == Maximum)
                this.ReleaseBrushes();
        }

        #endregion

        #region Component Designer generated code
        /// <summary> 
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            components = new System.ComponentModel.Container();
            // 
            // ProgressBar
            // 
            this.CausesValidation = false;
            this.Enabled = false;
            this.ForeColor = System.Drawing.SystemColors.Highlight;
            this.Name = "ProgressBar";
            this.Size = new System.Drawing.Size(432, 24);
        }
        #endregion
    }

    public class TestProgressBar : ColorProgressBar
    {
        #region Fields

        public static Color SuccessColor = Color.LimeGreen;
        public static Color FailureColor = Color.Red;
        public static Color IgnoredColor = Color.Yellow;

        private string statusText;

        #endregion

        #region Properties

        /// <summary>
        /// Gets the progress status text.
        /// </summary>
        public string StatusText
        {
            get { return statusText; }
            private set
            {
                statusText = value;
                Invalidate();
            }
        }

        #endregion

        #region Constructors

        public TestProgressBar()
        {
            Initialize(100);
        }

        #endregion

        #region Methods

        public void RegisterEvents(IEventHub eventHub)
        {
            eventHub.TestRunExecuted += eventHub_TestRunExecuted;
            eventHub.CompileFinished += eventHub_CompileFinished;
            eventHub.RunStarted += eventHub_RunStarted;
            eventHub.TestFinished += eventHub_TestFinished;
            eventHub.RunFinished += eventHub_RunFinished;
        }

        private void Initialize(int testCount)
        {
            Font = new Font(Font.FontFamily, 10);
            ForeColor = SuccessColor;
            Value = 0;
            Maximum = testCount > 0 ? testCount : 100;
            StatusText = String.Empty;
        }

        private void PaintText()
        {
            using (Graphics g = CreateGraphics())
            {
                PaintText(g);
            }
        }

        private void PaintText(Graphics g)
        {
            using (var brush = new SolidBrush(Color.White))
            {
                if (Value > 0)
                    g.DrawString(StatusText, Font, brush, 3, 3);
            }
        }

        #endregion

        #region Event Handlers

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            PaintText();
        }

        private void eventHub_TestRunExecuted(object sender, EventArgs e)
        {
            Initialize(0);
        }

        private void eventHub_CompileFinished(object sender, QuickTest.Core.Compilation.CompileFinishedEventArgs e)
        {
            if(!e.Succeeded)
            {
                ForeColor = FailureColor;
            }
        }

        private void eventHub_RunStarted(object sender, TestRunStartedEventArgs e)
        {
            Initialize(e.TestCount);
        }

        private void eventHub_TestFinished(object sender, TestEventArgs e)
        {
			this.InvokeIfNeeded(PerformStep);

            if (e.Result.IsFailure)
                ForeColor = FailureColor;

            StatusText = String.Format("Running test {0} of {1}", Value, Maximum);
        }

        private void eventHub_RunFinished(object sender, TestRunFinishedEventArgs e)
        {
            this.InvokeIfNeeded(Refresh);

            Value = Maximum;

            StatusText = String.Format("{0} passed, {1} failed, {2} not executed",
                                       e.SuccessCount, e.FailureCount, e.NotRunCount);
        }

        #endregion
    }
}
